﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using SimpleServiceBus.Bus;
using SimpleServiceBus.Bus.Pipeline;

namespace SimpleServiceBus.Endpoint.Management.Monitoring.Profiling
{
    public class PipelineProfiler
    {
        private readonly Stopwatch _pipelineTimer = new Stopwatch();
        private readonly Stopwatch _componentTimer = new Stopwatch();
        
        public PipelineProfiler(IMessagePipeline pipeline,bool profileComponents)
        {
            if (pipeline==null)
                throw new ArgumentNullException("pipeline");
            
            Pipeline = pipeline;
            ProfilePipelineComponents = profileComponents;

            Result = new PipelineProfilerResult();
                         
            BindToPipelineEvents();
        }

        public IMessagePipeline Pipeline { get; private set; }
        public PipelineProfilerResult Result { get; private set; }
        public bool ProfilePipelineComponents { get; private set; }

        private void BindToPipelineEvents()
        {
            Pipeline.PipelineEvents.PipelineStarted += OnPipelineStarted;
            Pipeline.PipelineEvents.ProcessingComponentStarted += OnComponentStarted;
            Pipeline.PipelineEvents.ProcessingComponentCompleted += OnComponentCompleted;
            Pipeline.PipelineEvents.PipelineCompleted += OnPipelineCompleted;
        }

        private void UnBindFromPipelineEvents()
        {
            Pipeline.PipelineEvents.PipelineStarted -= OnPipelineStarted;
            Pipeline.PipelineEvents.ProcessingComponentStarted -= OnComponentStarted;
            Pipeline.PipelineEvents.ProcessingComponentCompleted -= OnComponentCompleted;
            Pipeline.PipelineEvents.PipelineCompleted -= OnPipelineCompleted;
        }

        void OnPipelineStarted(object sender, MessagePipelineEventArgs e)
        {
            _pipelineTimer.Start();

            if (e.Message.Body != null)
                Result.MessageType = e.Message.Body.GetType();
            if (e.Message.Payload != null && e.Message.Payload.Length > 0)
                Result.MessageSize = e.Message.Payload.Length;

        }

        void OnComponentStarted(object sender, MessagePipelineEventArgs e)
        {
            if (!ProfilePipelineComponents)
                return;

            _componentTimer.Start();
        }

        void OnComponentCompleted(object sender, MessagePipelineEventArgs e)
        {
            if (!ProfilePipelineComponents)
                return;

            _componentTimer.Stop();

            Result.Components.Add
                (
                new PipelineComponentProfilerResult
                    {
                        ComponentName = e.Pipeline.CurrentComponent.Name,
                        ProcessingTime = _componentTimer.Elapsed
                    }
                );

            _componentTimer.Reset();
        }

        void OnPipelineCompleted(object sender, MessagePipelineEventArgs e)
        {
            _pipelineTimer.Stop();
            Result.ProcessingTime = _pipelineTimer.Elapsed;

            if (e.Message.Body != null)
                Result.MessageType = e.Message.Body.GetType();
            if (e.Message.Payload != null && e.Message.Payload.Length > 0)
                Result.MessageSize = e.Message.Payload.Length;

            UnBindFromPipelineEvents();
        }
    }
}
